package gis.lifay.sdk;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import hdf.object.Dataset;
import hdf.object.FileFormat;
import hdf.object.Group;
import hdf.object.HObject;
import hdf.object.h4.H4File;
import hdf.object.h4.H4SDS;
import hdf.view.Tools;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import java.awt.*;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.List;

import static hdf.view.Tools.findMinMax;
import static hdf.view.Tools.isNaNINF;

/**
 *@ClassName HDFUtil
 *@Description HDF工具类
 *@Author lifay
 *@Date 2021/11/1 11:30
 **/
public class HDFUtil {

    /**
     * 获取所有数据集
     * @param filePath hdf文件路径
     * @param close 是否关闭文件
     * @return
     * @throws Exception
     */
    public static List<H4SDS> getAllSubDataSets(String filePath, boolean close) throws Exception {
        FileFormat hdf4Format = initHDF4Format(filePath);
        TreeNode rootNode = hdf4Format.getRootNode();
        Group root = (Group) ((DefaultMutableTreeNode) rootNode).getUserObject();
        List<H4SDS> dataSets = new ArrayList<>();
        loopSubDataSets(root, dataSets, null);
        //System.out.println(dataSets.size());
        if (close) {
            hdf4Format.close();
        }
        return dataSets;
    }

    /**
     * 获取指定数据集
     * @param filePath hdf文件路径
     * @param close 是否关闭文件
     * @return
     * @throws Exception
     */
    public static List<H4SDS> getSubDataSet(String filePath, String datasetName, boolean close) throws Exception {
        FileFormat hdf4Format = initHDF4Format(filePath);
        TreeNode rootNode = hdf4Format.getRootNode();
        Group root = (Group) ((DefaultMutableTreeNode) rootNode).getUserObject();
        List<H4SDS> dataSets = new ArrayList<>();
        loopSubDataSets(root, dataSets, datasetName);
        //System.out.println(dataSets.size());
        if (close) {
            hdf4Format.close();
        }
        return dataSets;
    }

    /**
     * 获取渲染后的图片
     * @param dataset 数据集实例
     * @param palette 调色板数据
     * @return
     * @throws Exception
     */
    public static Image convertImage(H4SDS dataset, byte[][] palette) throws Exception {
        Object data = dataset.read();
        int h = (int) dataset.getDims()[0];
        int w = (int) dataset.getDims()[1];
        byte[] imageData = getBytes(data, dataset.getImageDataRange(), w, h, false, null, false, null, null);
        if (palette == null) {
            palette = Tools.createNaturePalette();
        }
        Image image = hdf.view.Tools.createIndexedImage(null, imageData, palette, w, h);
        return image;
    }

    /**
     * 获取渲染后的图片
     * @param filePath hdf文件路径
     * @param datasetName 数据集名称
     * @param palette 调色板数据
     * @return
     * @throws Exception
     */
    public static Image convertImage(String filePath, String datasetName, byte[][] palette) throws Exception {
        List<H4SDS> dataSetList = getSubDataSet(filePath, datasetName, false);
        if (ObjectUtil.isEmpty(dataSetList)) {
            throw new Exception("未获取到数据集!");
        }
        H4SDS dataset = dataSetList.get(0);
        Object data = dataset.read();
        int h = (int) dataset.getDims()[0];
        int w = (int) dataset.getDims()[1];
        byte[] imageData = getBytes(data, dataset.getImageDataRange(), w, h, false, null, false, null, null);
        if (palette == null) {
            palette = Tools.createNaturePalette();
        }
        Image image = hdf.view.Tools.createIndexedImage(null, imageData, palette, w, h);
        return image;
    }

    /**
     * 获取渲染后的图片
     * @param dataset
     * @param palettePath 调色板文件路径
     * @return
     * @throws Exception
     */
    public static Image convertImage(H4SDS dataset, String palettePath) throws Exception {
        //return convertImage(dataset, hdf.view.Tools.readPalette(palettePath));
        return convertImage(dataset, Tools.createNaturePalette());
    }

    /**
     * 初始化HDF4库
     * @return
     * @throws Exception
     */
    public static FileFormat initHDF4Format(String filePath) throws Exception {
        // retrieve an instance of H4File
        H4File fileFormat = new H4File(filePath);
        //FileFormat fileFormat = FileFormat.getFileFormat(FileFormat.FILE_TYPE_HDF4);
        if (fileFormat == null) {
            throw new Exception("Cannot find HDF4 FileFormat.");
        }
        fileFormat.open();
        return fileFormat;
    }

    /**
     * 递归获取数据集
     * @param root HDF对象
     * @param dataSets 数据集列表
     * @param datasetName 数据集名称,可空
     * @throws Exception
     */
    private static void loopSubDataSets(HObject root, List<H4SDS> dataSets, String datasetName) throws Exception {
        if (root instanceof Group) {
            Group group = (Group) root;
            for (HObject hObject : group.getMemberList()) {
                loopSubDataSets(hObject, dataSets, datasetName);
            }
        } else if (root instanceof Dataset && StrUtil.isNotBlank(datasetName) && root.getName().equals(datasetName)) {
            dataSets.add((H4SDS) root);
        }
    }


    /**
     *
     * @param var0 data
     * @param var1 range
     * @param var2 w
     * @param var3 h
     * @param var4 isTransposed
     * @param var5 invalidValues
     *      *            the list of invalid values.
     * @param var6 convertByteData
     *      *            the converted data out.
     * @param var7 byteData
     *      *            the data in.
     * @param var8 list
     *      *            the list of integers.
     * @return
     */
    public static byte[] getBytes(Object var0, double[] var1, int var2, int var3, boolean var4, List<Number> var5, boolean var6, byte[] var7, List<Integer> var8) {
        double[] var9 = null;
        if (var0 != null && var2 > 0 && var3 > 0) {
            if (!var0.getClass().isArray()) {
                return null;
            } else {
                double var10 = 1.7976931348623157E308D;
                double var12 = -1.7976931348623157E308D;
                double var14 = 1.0D;
                String var16 = var0.getClass().getName();
                char var17 = var16.charAt(var16.lastIndexOf("[") + 1);
                int var18 = Array.getLength(var0);
                if (var1 == null) {
                    var1 = new double[2];
                    var1[0] = var1[1] = 0.0D;
                }

                if (var17 == 'B') {
                    return convertByteData((byte[]) ((byte[]) var0), var1, var2, var3, var4, var9, var6, var7, var8);
                } else {
                    if (var7 == null || var18 != var7.length) {
                        var7 = new byte[var18];
                    }

                    if (var1[0] == var1[1]) {
                        findMinMax(var0, var1, var9);
                    }

                    var10 = var1[0];
                    var12 = var1[1];
                    int var19;
                    int var20;
                    if (var5 != null && var5.size() > 0) {
                        var19 = var5.size();
                        var9 = new double[var19];

                        for (var20 = 0; var20 < var19; ++var20) {
                            var9[var20] = ((Number) var5.get(var20)).doubleValue();
                        }
                    }

                    var14 = var10 == var12 ? 1.0D : 255.0D / (var12 - var10);
                    boolean var28 = false;
                    boolean var29 = false;
                    int var23;
                    int var24;
                    int var25;
                    int var26;
                    switch (var17) {
                        case 'D':
                            double[] var33 = (double[]) ((double[]) var0);

                            for (var26 = 0; var26 < var3; ++var26) {
                                for (int var27 = 0; var27 < var2; ++var27) {
                                    var19 = var20 = var27 * var3 + var26;
                                    if (var4) {
                                        var20 = var26 * var2 + var27;
                                    }

                                    var7[var20] = toByte(var33[var19], var14, var10, var12, var9, var19, var8);
                                }
                            }

                            return var7;
                        case 'F':
                            float[] var32 = (float[]) ((float[]) var0);

                            for (var25 = 0; var25 < var3; ++var25) {
                                for (var26 = 0; var26 < var2; ++var26) {
                                    var19 = var20 = var26 * var3 + var25;
                                    if (var4) {
                                        var20 = var25 * var2 + var26;
                                    }

                                    var7[var20] = toByte((double) var32[var19], var14, var10, var12, var9, var19, var8);
                                }
                            }

                            return var7;
                        case 'I':
                            int[] var30 = (int[]) ((int[]) var0);

                            for (var23 = 0; var23 < var3; ++var23) {
                                for (var24 = 0; var24 < var2; ++var24) {
                                    var19 = var20 = var24 * var3 + var23;
                                    if (var4) {
                                        var20 = var23 * var2 + var24;
                                    }

                                    var7[var20] = toByte((double) var30[var19], var14, var10, var12, var9, var19, var8);
                                }
                            }

                            return var7;
                        case 'J':
                            long[] var31 = (long[]) ((long[]) var0);

                            for (var24 = 0; var24 < var3; ++var24) {
                                for (var25 = 0; var25 < var2; ++var25) {
                                    var19 = var20 = var25 * var3 + var24;
                                    if (var4) {
                                        var20 = var24 * var2 + var25;
                                    }

                                    var7[var20] = toByte((double) var31[var19], var14, var10, var12, var9, var19, var8);
                                }
                            }

                            return var7;
                        case 'S':
                            short[] var21 = (short[]) ((short[]) var0);

                            for (int var22 = 0; var22 < var3; ++var22) {
                                for (var23 = 0; var23 < var2; ++var23) {
                                    var19 = var20 = var23 * var3 + var22;
                                    if (var4) {
                                        var20 = var22 * var2 + var23;
                                    }

                                    var7[var20] = toByte((double) var21[var19], var14, var10, var12, var9, var19, var8);
                                }
                            }

                            return var7;
                        default:
                            var7 = null;
                            return var7;
                    }
                }
            }
        } else {
            return null;
        }
    }

    private static byte[] convertByteData(byte[] var0, double[] var1, int var2, int var3, boolean var4, Object var5, boolean var6, byte[] var7, List<Integer> var8) {
        double var9 = 1.7976931348623157E308D;
        double var11 = -1.7976931348623157E308D;
        double var13 = 1.0D;
        if (var0 == null) {
            return null;
        } else {
            if (var6 && var1[0] == var1[1]) {
                findMinMax(var0, var1, var5);
            }

            if (var1[0] == 0.0D && var1[1] == 255.0D) {
                var6 = false;
            }

            if (!var6 && !var4) {
                if (var7 != null && var7.length == var0.length) {
                    System.arraycopy(var0, 0, var7, 0, var0.length);
                    return var7;
                } else {
                    return var0;
                }
            } else {
                if (var7 == null || var0 == var7) {
                    var7 = new byte[var0.length];
                }

                int var19;
                int var20;
                if (!var6) {
                    var1[0] = 0.0D;
                    var1[1] = 255.0D;
                    if (var4) {
                        for (var19 = 0; var19 < var3; ++var19) {
                            for (var20 = 0; var20 < var2; ++var20) {
                                var7[var19 * var2 + var20] = var0[var20 * var3 + var19];
                            }
                        }
                    }

                    return var7;
                } else {
                    var9 = var1[0];
                    var11 = var1[1];
                    var13 = var9 == var11 ? 1.0D : 255.0D / (var11 - var9);
                    boolean var15 = false;
                    boolean var16 = false;

                    for (int var17 = 0; var17 < var3; ++var17) {
                        for (int var18 = 0; var18 < var2; ++var18) {
                            var19 = var20 = var18 * var3 + var17;
                            if (var4) {
                                var20 = var17 * var2 + var18;
                            }

                            if (!((double) var0[var19] > var11) && !((double) var0[var19] < var9)) {
                                var7[var20] = (byte) ((int) (((double) var0[var19] - var9) * var13));
                            } else {
                                var7[var20] = 0;
                                if (var8 != null) {
                                    var8.add(var19);
                                }
                            }
                        }
                    }

                    return var7;
                }
            }
        }
    }

    private static byte toByte(double var0, double var2, double var4, double var6, double[] var8, int var9, List<Integer> var10) {
        boolean var11 = false;
        byte var12;
        if (!(var0 < var4) && !(var0 > var6) && !isFillValue(var0, var8) && !isNaNINF(var0)) {
            var12 = (byte) ((int) ((var0 - var4) * var2));
        } else {
            var12 = 0;
            if (var10 != null) {
                var10.add(var9);
            }
        }

        return var12;
    }

    private static boolean isFillValue(double var0, double[] var2) {
        if (var2 == null) {
            return false;
        } else {
            for (int var3 = 0; var3 < var2.length; ++var3) {
                if (var2[var3] == var0) {
                    return true;
                }
            }

            return false;
        }
    }

}
